package cn.bandeqing.user.user.rpc.impl;

import cn.bandeqing.authority.entity.Authority;
import cn.bandeqing.authority.service.IAuthorityService;
import cn.bandeqing.base.domain.AdminUserStore;
import cn.bandeqing.base.domain.CurrentAdminUser;
import cn.bandeqing.base.resp.Resp;
import cn.bandeqing.base.resp.Table;
import cn.bandeqing.base.util.PasswordUtil;
import cn.bandeqing.frame.exception.CodeException;
import cn.bandeqing.role.entity.Role;
import cn.bandeqing.role.service.IRoleService;
import cn.bandeqing.roleAuthority.entity.RoleAuthority;
import cn.bandeqing.roleAuthority.service.IRoleAuthorityService;
import cn.bandeqing.store.service.IStoreService;
import cn.bandeqing.stp.StpAdminUtil;
import cn.bandeqing.stp.StpWorkerUtil;
import cn.bandeqing.user.entity.UsStatus;
import cn.bandeqing.user.entity.UsType;
import cn.bandeqing.user.entity.User;
import cn.bandeqing.user.req.*;
import cn.bandeqing.user.resp.*;
import cn.bandeqing.user.rpc.IUserRpc;
import cn.bandeqing.user.service.IUserService;
import cn.bandeqing.userRole.entity.UserRole;
import cn.bandeqing.userRole.service.IUserRoleService;
import cn.bandeqing.userWallet.entity.UserWallet;
import cn.bandeqing.userWallet.service.IUserWalletService;
import cn.bandeqing.userWalletRecord.entity.UserWalletRecord;
import cn.bandeqing.userWalletRecord.entity.UwrStatus;
import cn.bandeqing.userWalletRecord.entity.UwrType;
import cn.bandeqing.userWalletRecord.service.IUserWalletRecordService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.Row;
import io.github.linpeilie.Converter;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboService;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.collection.set.SetUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.RandomUtil;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import static cn.bandeqing.authority.entity.table.AuthorityTableDef.AUTHORITY;
import static cn.bandeqing.role.entity.table.RoleTableDef.ROLE;
import static cn.bandeqing.roleAuthority.entity.table.RoleAuthorityTableDef.ROLE_AUTHORITY;
import static cn.bandeqing.store.entity.table.StoreTableDef.STORE;
import static cn.bandeqing.user.entity.table.UserTableDef.USER;
import static cn.bandeqing.userRole.entity.table.UserRoleTableDef.USER_ROLE;
import static cn.bandeqing.userWallet.entity.table.UserWalletTableDef.USER_WALLET;

@DubboService
public class UserRpcImpl implements IUserRpc {

    @Resource
    private IUserService userService;
    @Resource
    private IRoleService roleService;
    @Resource
    private IUserRoleService userRoleService;
    @Resource
    private IRoleAuthorityService roleAuthorityService;
    @Resource
    private IAuthorityService authorityService;
    @Resource
    private IUserWalletService userWalletService;
    @Resource
    private IUserWalletRecordService userWalletRecordService;
    @Resource
    private IStoreService storeService;
    @Resource
    private Converter converter;

    @Override
    public LoginResp login(LoginReq req) {
        LoginResp resp = userService.getOneAs(QueryWrapper.create()
                .where(USER.US_LOGIN_NAME.eq(req.getUsername())), LoginResp.class);

        Assert.notNull(resp, () -> new CodeException("用户不存在"));

        String saltPwd = PasswordUtil.pwdSalt(req.getUsername(), req.getPassword(), resp.getUsPasswordSalt());

        Assert.isTrue(saltPwd.equals(resp.getUsPassword()), () -> new CodeException("密码错误"));

        Assert.isTrue(UsStatus.ok.getValue().equals(resp.getUsStatus()), () -> new CodeException("用户状态异常"));

        // 查询账号类型判断是否有店铺
        if (UsType.city.getValue().equals(resp.getUsType()) || UsType.shop.getValue().equals(resp.getUsType())) {

            List<AdminUserStore> adminUserStores = storeService.listAs(QueryWrapper.create()
                    .where(STORE.CITY_PARTNER_US_ID.eq(resp.getUsId())
                            .or(STORE.SHOPKEEPER_US_ID.eq(resp.getUsId()))
                            .or(STORE.SHOPKEEPER_US_ID.eq(resp.getUsId()))), AdminUserStore.class);

            Assert.notEmpty(adminUserStores, () -> new CodeException("账户下未配置店铺，请联系管理员配置店铺后重试。"));
            resp.setStores(adminUserStores);

//            long count = storeService.count(QueryWrapper.create()
//                    .where(STORE.CITY_PARTNER_US_ID.eq(resp.getUsId())
//                            .or(STORE.SHOPKEEPER_US_ID.eq(resp.getUsId()))
//                            .or(STORE.SHOPKEEPER_US_ID.eq(resp.getUsId()))));
//            Assert.isTrue(count > 0, () -> new CodeException("账户下未配置店铺，请联系管理员配置店铺后重试。"));

        }

        List<Role> roles = roleService.listAs(QueryWrapper.create()
                .select(ROLE.ROLE_CODE, ROLE.ROLE_ID)
                .innerJoin(USER_ROLE).on(ROLE.ROLE_ID.eq(USER_ROLE.ROLE_ID))
                .where(USER_ROLE.USER_ID.eq(resp.getUsId())), Role.class);

        List<Long> roleIds = roles.stream().map(Role::getRoleId).toList();

        if (!resp.getSuperAdmin()) {
            // 用户没有role就不让登录 不是超管
            Assert.notEmpty(roleIds, () -> new CodeException("账户下未配置角色，请联系管理配置后再试。"));
        }

        List<String> roleCodes = roles.stream().map(Role::getRoleCode).toList();

        resp.setRoles(roleCodes);

        if (!roles.isEmpty()) {

            List<Authority> all = authorityService.list(QueryWrapper.create()
                    .select(AUTHORITY.AUTHORITY_ID, AUTHORITY.AUTHORITY_PID, AUTHORITY.AUTHORITY_CODE));

            List<Authority> roleAuths = authorityService.list(QueryWrapper.create()
                    .select(AUTHORITY.AUTHORITY_ID, AUTHORITY.AUTHORITY_PID, AUTHORITY.AUTHORITY_CODE)
                    .innerJoin(ROLE_AUTHORITY).on(ROLE_AUTHORITY.AUTHORITY_ID.eq(AUTHORITY.AUTHORITY_ID))
                    .where(ROLE_AUTHORITY.ROLE_ID.in(roleIds)));

            HashSet<String> set = SetUtil.of();
            for (Authority roleAuth : roleAuths) {
                findParent(set, roleAuth, all);
            }

            resp.setAuths(ListUtil.of(set));
        }

        return resp;
    }

    public void findParent(HashSet<String> set, Authority self, List<Authority> all) {
        for (Authority parent : all) {
            if (self.getAuthorityPid().equals(parent.getAuthorityId())) {
                set.add(self.getAuthorityCode());
                set.add(parent.getAuthorityCode());
                findParent(set, parent, all);
            }
        }
    }

    @Override
    public Resp<Table<UserTable>> table(UserTableReq req) {

        QueryWrapper qw = QueryWrapper.create()
                .where(USER.US_ID.eq(req.getUsId()))
                .where(USER.US_UUID.eq(req.getUsUuid()))
                .where(USER.US_NICK_NAME.eq(req.getUsNickName()))
                .where(USER.US_REAL_NAME.eq(req.getUsRealName()))
                .where(USER.US_LOGIN_NAME.eq(req.getUsLoginName()))
                .where(USER.US_TYPE.eq(req.getUsType()))
                .where(USER.US_STATUS.eq(req.getUsStatus()))
                .orderBy(USER.US_ID.desc());

        CurrentAdminUser current = StpAdminUtil.getCurrent();
        if (!current.getUsType().equals(UsType.company.getValue())) {
            qw.where(USER.CREATE_USER_ID.eq(current.getUsId()));
        }

        Page<UserTable> page = userService.pageAs(Page.of(req.getCurrent(), req.getPageSize()), qw, UserTable.class);
        return Resp.success(Table.of(page.getRecords(), page.getTotalRow()));
    }

    @Override
    @Transactional
    public void saveOrUpdate(UserSaveOrUpdateReq req) {
        User user = converter.convert(req, User.class);

        boolean add = req.getUsId() == null;
        if (add) {
            // 新增
            user.setUsUuid(RandomUtil.randomNumbers(6));
            user.setUsPasswordSalt(RandomUtil.randomNumbers(6));
            user.setUsPassword(PasswordUtil.pwdSalt(req.getUsLoginName(), req.getUsPassword(), user.getUsPasswordSalt()));
            user.setUsStatus(UsStatus.ok);
        }

        boolean b = userService.saveOrUpdate(user);
        Assert.isTrue(b, () -> new CodeException("userService.saveOrUpdate(user)"));

        if (add) {
            boolean save = userWalletService.save(UserWallet.builder()
                    .usId(user.getUsId())
                    .usNickName(user.getUsNickName())
                    .build());
            Assert.isTrue(save, CodeException::new);
        } else {

            // 编辑需要踢出用户
            StpAdminUtil.logout(user.getUsId());
        }
    }

    @Override
    public Resp<List<RolesResp>> roles() {
        List<RolesResp> resps = roleService.listAs(QueryWrapper.create()
                .select(ROLE.ROLE_ID.as(RolesResp::getValue),
                        ROLE.ROLE_NAME.as(RolesResp::getLabel)), RolesResp.class);

        return Resp.success(resps);
    }

    @Override
    public Resp<Void> userRole(UserRoleReq req) {
        userRoleService.remove(QueryCondition.create(USER_ROLE.USER_ID, req.getUsId()));
        ArrayList<UserRole> objects = ListUtil.of();
        req.getRoleIds().forEach(roleId -> objects.add(UserRole.builder().userId(req.getUsId()).roleId(roleId).build()));
        userRoleService.saveBatch(objects);
        return Resp.success();
    }

    @Override
    public Resp<List<Long>> userRoles(UserRolesReq req) {
        List<Long> roleIds = userRoleService.listAs(QueryWrapper.create()
                .select(USER_ROLE.ROLE_ID)
                .where(USER_ROLE.USER_ID.eq(req.getUserId())), Long.class);
        return Resp.success(roleIds);
    }

    @Override
    public Resp<UserBasicResp> userBasic() {
        UserBasicResp ubr = userService.getOneAs(QueryWrapper.create()
                .where(USER.US_ID.eq(StpAdminUtil.getLoginId())), UserBasicResp.class);
        return Resp.success(ubr);
    }

    @Override
    public Resp<Void> userUpdateBasic(UserUpdateBasicReq req) {
        User user = converter.convert(req, User.class);
        user.setUsId(StpAdminUtil.getLoginId());
        Assert.isTrue(userService.updateById(user), CodeException::new);
        return Resp.success();
    }

    @Override
    public Resp<UserBalanceResp> balance() {
        UserBalanceResp ubr = userWalletService.getOneAs(QueryWrapper.create()
                .where(USER_WALLET.US_ID.eq(StpAdminUtil.getLoginId())), UserBalanceResp.class);
        return Resp.success(ubr);
    }

    @Override
    public Resp<Void> withdraw(UserWithdrawReq req) {
        UserWallet uw = userWalletService.getById(StpAdminUtil.getLoginId());

        Assert.isTrue(uw.getUsBalance() > req.getMoney(), () -> new CodeException("余额不足"));

        Double usBalance = uw.getUsBalance();
        Double after = usBalance - req.getMoney();

        Assert.isTrue(userWalletService.updateById(UserWallet.builder()
                .usId(uw.getUsId())
                .usBalance(after)
                .usTotalCashMoney(uw.getUsTotalCashMoney() + req.getMoney())
                .build()), CodeException::new);

        Assert.isTrue(userWalletRecordService.save(UserWalletRecord.builder()
                .usId(uw.getUsId())
                .usNickName(uw.getUsNickName())
                .uwrType(UwrType.cash)
                .beforeMoney(usBalance)
                .changeMoney(req.getMoney())
                .afterMoney(after)
                .uwrStatus(UwrStatus.await)
                .uwrDesc("提现申请")
                .build()));

        return Resp.success();
    }

    @Override
    public Resp<Void> changePwd(ChangePwdReq req) {
        Assert.isFalse(req.getOldPwd().equals(req.getNewPwd()), () -> new CodeException("新旧密码不可一致"));
        Assert.isTrue(req.getNewPwd().equals(req.getVerifyNewPwd()), () -> new CodeException("新密码两次输入不一致"));

        User user = userService.getById(StpAdminUtil.getLoginId());
        String oldPwd = PasswordUtil.pwdSalt(user.getUsLoginName(), req.getOldPwd(), user.getUsPasswordSalt());
        Assert.isTrue(oldPwd.equals(user.getUsPassword()), () -> new CodeException("旧密码错误"));

        String newSalt = RandomUtil.randomNumbers(6);
        String newPwd = PasswordUtil.pwdSalt(user.getUsLoginName(), req.getNewPwd(), newSalt);
        Assert.isTrue(userService.updateById(User.builder()
                .usId(user.getUsId())
                .usPasswordSalt(newSalt)
                .usPassword(newPwd)
                .build()), CodeException::new);

        StpAdminUtil.logout();

        return Resp.success();
    }

}
